﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Authentication;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

namespace Synology.AudioStationApi
{
    public class AudioStationSession : IAudioStationSession
    {
        #region Delegates

        public delegate void LoginCompletedEventHandler(object sender, LoginCompletedEventArgs eventArgs);

        public delegate void SearchAsyncCompletedEventHandler(object sender, SearchAsyncCompletedEventArgs e);

        #endregion

        private readonly string _host;
        private readonly int _port;
        private readonly AutoResetEvent _synchronousResetEvent = new AutoResetEvent(false);
        private object[] _asyncOperationArguments;
        private bool _queryLock;
        private SynoQueryResult _queryResult;

        // The token used with every command issued to the syno server.
        private string _tokenCookie;
        private string _url = string.Empty;

        /// <summary> 
        /// Initializes a new instance of the <see cref="AudioStationSession"/> class.
        /// </summary>
        /// <param name="host">The host.</param>
        public AudioStationSession(string host, string login, string password)
            : this(host, 80, login, password)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AudioStationSession"/> class.
        /// </summary>
        /// <param name="host">The host.</param>
        /// <param name="port">The port.</param>
        /// <param name="login">The login.</param>
        /// <param name="password">The password.</param>
        public AudioStationSession(string host, int port, string login, string password)
        {
            if (host == null) throw new ArgumentNullException("host");
            _host = host;
            _port = port;
            _url = "http://" + _host + ":" + _port;
            _tokenCookie = GetToken(login, password);
        }

        #region IAudioStationSession Members

        public event LoginCompletedEventHandler LoginCompleted;

        public string Login(string login, string password)
        {
            if (login == null) throw new ArgumentNullException("login");
            if (password == null) throw new ArgumentNullException("password");


            var client = new WebClient();

            Uri uri =
                new UriBuilder
                    {
                        Host = _host,
                        Path = @"/webman/modules/login.cgi",
                        Query = string.Format("username={0}&passwd={1}", login, password),
                        Port = _port
                    }.Uri;
            client.DownloadString(uri);

            _tokenCookie = getTokenCookie(client);
            return _tokenCookie;
        }

        public void LoginAsync(string login, string password, object[] arguments)
        {
            if (login == null) throw new ArgumentNullException("login");
            if (password == null) throw new ArgumentNullException("password");

            if (beginExclusiveQuery())
            {
                throw new ConstraintException(
                    "Only one concurrent query is accepted Wait until the previous query is finished being handled and issue a new query after that.");
            }

            WebClient client = new WebClient();

            _asyncOperationArguments = arguments;

            client.DownloadStringCompleted += onLoginRequestCompleted;

            Uri uri =
                new UriBuilder
                    {
                        Host = _host,
                        Path = @"/webman/modules/login.cgi",
                        Query = string.Format("username={0}&passwd={1}", login, password),
                        Port = _port
                    }.Uri;
            client.DownloadStringAsync(uri);
        }

        public IAsyncResult SearchAsync(string query, QueryType queryType)
        {
            if (query == null) throw new ArgumentNullException("query");
            if (_tokenCookie == null)
                throw new InvalidOperationException(
                    "The Login method should be called before calling the Search method.");

            if (beginExclusiveQuery())
            {
                throw new ConstraintException(
                    "Only one concurrent query is accepted Wait until the previous query is finished being handled and issue a new query after that.");
            }

            WebClient client = new WebClient();
            string queryString;
            switch (queryType)
            {
                case QueryType.AllMusic:
                    client.DownloadStringCompleted += queryCompleted;
                    queryString = string.Format(
                        "action=search&target=musiclib_root&server=musiclib_root&category=all&keyword={0}&start=0",
                        query);
                    break;
                case QueryType.AudioRoot:
                    client.DownloadStringCompleted += audioRootQueryCompleted;
                    queryString = "action=browse&target=musiclib_root&server=musiclib_root";
                    break;
                case QueryType.Artist:
                    client.DownloadStringCompleted += artistQueryCompleted;
                    queryString = string.Format(
                        "sort=title&dir=ASC&action=browse&target=musiclib_music_aa&server=musiclib_music_aa&category=&keyword={0}&start=0&limit=50",
                        query);
                    break;
                case QueryType.ArtistID:
                    client.DownloadStringCompleted += artistIDQueryCompleted;
                    queryString = string.Format(
                        "action=browse&target={0}&server=musiclib_music_aa&category=&keyword=&start=0&sort=title&dir=ASC&limit=50",
                        query);
                    break;
                case QueryType.Album:
                    client.DownloadStringCompleted += albumQueryCompleted;
                    queryString = string.Format(
                        "sort=title&dir=ASC&action=browse&target=musiclib_music_album&server=musiclib_root&category=&keyword={0}&start=0&limit=150",
                        query);
                    break;
                case QueryType.AlbumID:
                    client.DownloadStringCompleted += albumIDQueryCompleted;
                    queryString = string.Format(
                        "sort=album&dir=ASC&action=browse&target={0}&server=musiclib_music_aa&category=&keyword=&start=0&limit=50",
                        query);
                    break;
                default:
                    throw new ArgumentOutOfRangeException("queryType");
            }

            Uri uri = new UriBuilder
                          {
                              Host = _host,
                              Port = _port,
                              Path = "/audio/webUI/audio_browse.cgi",
                              Query = queryString
                          }.Uri;

            client.Headers.Add("Cookie", _tokenCookie);
            client.DownloadStringAsync(uri);

            _queryResult = new SynoQueryResult(_synchronousResetEvent);

            return _queryResult;
        }

        public event SearchAsyncCompletedEventHandler SearchAsyncCompleted;


        /// <summary>
        /// Gets the remote file network stream.
        /// </summary>
        /// <param name="res">The "res" for the file, that is : the songpath.</param>
        /// <param name="stream">The stream.</param>
        /// <remarks>The caller is responsible for closing the stream after the call to DownloadFile returns</remarks>
        public void GetFileStream(string res, out Stream stream)
        {
            WebClient client = new WebClient();
            if (res == null) throw new ArgumentNullException("res");

            //todo : refactor this : put everywhere at one place.
            client.Headers.Set("Cookie", _tokenCookie);

            Uri uri =
                new UriBuilder
                    {
                        Host = _host,
                        Port = _port,
                        Path = "/audio/webUI/audio_stream.cgi",
                        Query = string.Format("action=streaming&songpath={0}", res)
                    }.Uri;
            stream = client.OpenRead(uri);
        }

        public void Search()
        {
            throw new NotImplementedException();
        }

        public void LoginAsync(string login, string password)
        {
            LoginAsync(login, password, new object[0]);
        }

        #endregion

        private bool beginExclusiveQuery()
        {
            var oldLock = _queryLock;
            _queryLock = true;
            return oldLock;
        }

        private void onLoginRequestCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            var client = (WebClient) sender;

            // Clean event registrations : Since we use the same webclient object for all requests !
            client.DownloadStringCompleted -= onLoginRequestCompleted;

            // rethrow exception from the downloader if any.
            if (e.Error != null) throw e.Error;

            string tokenCookie = getTokenCookie(client);

            // The event is ready to be handled : let's do it !
            onLoginAsyncCompleted(new LoginCompletedEventArgs(tokenCookie, client)
                                      {Parameters = _asyncOperationArguments});

            endExclusiveQuery();
        }

        private void endExclusiveQuery()
        {
            _queryLock = false;
        }


        private string getTokenCookie(WebClient client)
        {
            string tokenCookie = null;
            bool hasCookie = client.ResponseHeaders.AllKeys.Contains("Set-Cookie");
            if (!hasCookie)
            {
                // The credentials were not valid.
                throw new InvalidCredentialException();
            }

            string[] cookies = client.ResponseHeaders["Set-Cookie"].Split(';');

            // find the "ID" Cookie.
            foreach (string item in cookies)
            {
                string[] cookie = item.Split('=');
                if (cookie[0].Equals("id", StringComparison.OrdinalIgnoreCase))
                {
                    tokenCookie = item;
                    break;
                }
            }
            return tokenCookie;
        }

        private void onLoginAsyncCompleted(LoginCompletedEventArgs loginCompletedEventArgs)
        {
            if (loginCompletedEventArgs == null) throw new ArgumentNullException("loginCompletedEventArgs");

            _tokenCookie = loginCompletedEventArgs.TokenCookie;
            if (LoginCompleted != null)
            {
                LoginCompleted(this, loginCompletedEventArgs);
            }
            loginCompletedEventArgs.Client.Dispose();            
        }


        private void audioRootQueryCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            WebClient client = new WebClient();
            client.DownloadStringCompleted -= albumIDQueryCompleted;
            IEnumerable<SynoItem> categories;

            long count;
            parseSynologyAlbums(e.Result, out categories, out count);

            onSearchAsyncCompleted(new SearchAsyncCompletedEventArgs(categories));
            endExclusiveQuery();
        }


        private void parseSynologyArtists(string result, out IEnumerable<SynoItem> artists, out long count)
        {
            JObject jObject = JObject.Parse(result);
            count = 0L;
            artists = null;
            artists = from album in jObject["items"]
                      select new SynoItem
                                 {
                                     AlbumArtUrl = album["albumArtURL"].Value<string>(),
                                     Icon = album["icon"].Value<string>(),
                                     IsContainer = album["is_container"].Value<bool>(),
                                     IsTrack = album["is_track"].Value<bool>(),
                                     ItemID = album["item_id"].Value<string>(),
                                     ItemPid = album["item_pid"].Value<string>(),
                                     Sequence = album["sequence"].Value<int>(),
                                     Support = album["support"].Value<bool>(),
                                     Title = album["title"].Value<string>()
                                 };
            count = artists.Count();
        }

        private void parseSynologyFiles(string s, out IEnumerable<SynoTrack> files, out long total)
        {
            JObject o = JObject.Parse(s);
            JToken items = o["items"];
            total = o["total"].Value<long>();
            files = from p in items
                    select new SynoTrack
                               {
                                   Album = p["album"].Value<string>(),
                                   AlbumArtUrl = _url + "/audio/"+ p["albumArtURL"].Value<string>(),
                                   Artist = p["artist"].Value<string>(),
                                   Bitrate = p["bitrate"].Value<long>(),
                                   Channels = p["channels"].Value<int>(),
                                   //Class = p["class"].Value<string>(),
                                   Disc = p["disc"].Value<int>(),
                                   Duration = TimeSpan.FromSeconds(p["duration"].Value<int>()),
                                   //DateTime.ParseExact(p["duration"].Value<string>(), "m:ss", CultureInfo.InvariantCulture,DateTimeStyles.AllowLeadingWhite).TimeOfDay, // doesn't work if m > 59
                                   Genre = p["genre"].Value<string>(),
                                   Icon = p["icon"].Value<string>(),
                                   IsContainer = p["is_container"].Value<bool>(),
                                   IsTrack = p["is_track"].Value<bool>(),
                                   ItemID = p["item_id"].Value<string>(),
                                   ItemPid = p["item_pid"].Value<string>(),
                                   //ProtocolInfo = p["protocolinfo"].Value<string>(),
                                   Res = p["res"].Value<string>(),
                                   Sample = p["sample"].Value<long>(),
                                   Sequence = p["sequence"].Value<int>(),
                                   Size = long.Parse(p["size"].Value<string>()),
                                   Support = p["support"].Value<bool>(),
                                   Title = p["title"].Value<string>(),
                                   Track = p["track"].Value<int>(),
                                   Year = p["year"].Value<int>()
                               }
                ;
        }

        private void parseSynologyAlbums(string result, out IEnumerable<SynoItem> albums, out long count)
        {
            JObject jObject = JObject.Parse(result);
            count = 0L;
            albums = null;
            albums = from album in jObject["items"]
                     select new SynoItem
                                {
                                    AlbumArtUrl = album["albumArtURL"].Value<string>(),
                                    Icon = album["icon"].Value<string>(),
                                    IsContainer = album["is_container"].Value<bool>(),
                                    IsTrack = album["is_track"].Value<bool>(),
                                    ItemID = album["item_id"].Value<string>(),
                                    ItemPid = album["item_pid"].Value<string>(),
                                    Sequence = album["sequence"].Value<int>(),
                                    Support = album["support"].Value<bool>(),
                                    Title = album["title"].Value<string>()
                                };
        }

        private void albumIDQueryCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            WebClient client = new WebClient();
            client.DownloadStringCompleted -= albumIDQueryCompleted;
            IEnumerable<SynoTrack> tracks;


            long count;
            parseSynologyFiles(e.Result, out tracks, out count);

            onSearchAsyncCompleted(new SearchAsyncCompletedEventArgs(tracks));
            endExclusiveQuery();
        }

        private void albumQueryCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            WebClient client = new WebClient();

            client.DownloadStringCompleted -= albumQueryCompleted;

            IEnumerable<SynoItem> albums;


            long count;
            parseSynologyAlbums(e.Result, out albums, out count);

            onSearchAsyncCompleted(new SearchAsyncCompletedEventArgs(albums));
            endExclusiveQuery();
        }

        // returns the specified albums for a specified artist.
        private void artistIDQueryCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            WebClient client = new WebClient();
            client.DownloadStringCompleted -= artistIDQueryCompleted;
            IEnumerable<SynoItem> albums;


            long count;
            parseSynologyAlbums(e.Result, out albums, out count);

            onSearchAsyncCompleted(new SearchAsyncCompletedEventArgs(albums));

            endExclusiveQuery();
        }


        // returns a list of artists
        private void artistQueryCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            WebClient client = new WebClient();
            client.DownloadStringCompleted -= artistQueryCompleted;
            IEnumerable<SynoItem> artists;
            long count;

            parseSynologyArtists(e.Result, out artists, out count);

            onSearchAsyncCompleted(new SearchAsyncCompletedEventArgs(artists));

            endExclusiveQuery();
        }


        private void queryCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            WebClient client = new WebClient();
            // this should only happen once, if it needs to happen one more time,
            // it will get re-registered, so we'd better clean after we're done.
            client.DownloadStringCompleted -= queryCompleted;

            IEnumerable<SynoTrack> tracks;
            long count;
            parseSynologyFiles(e.Result, out tracks, out count);


            onSearchAsyncCompleted(new SearchAsyncCompletedEventArgs(tracks));
        }


        protected void onSearchAsyncCompleted(SearchAsyncCompletedEventArgs searchAsyncEventArgs)
        {
            _queryResult.AsyncState = searchAsyncEventArgs.Items;
            if (SearchAsyncCompleted != null)
            {
                SearchAsyncCompleted(this, searchAsyncEventArgs);
            }
            ((AutoResetEvent) _queryResult.AsyncWaitHandle).Set();
        }

        #region Nested type: SynoQueryResult

        /// <summary>
        /// When issuing an asynchronous query on a Syno service, this class can be used to retrieve the result value.
        /// </summary>
        public class SynoQueryResult : IAsyncResult
        {
            private object _asyncState;

            public SynoQueryResult(WaitHandle waitHandle)
            {
                AsyncWaitHandle = waitHandle;
            }

            #region IAsyncResult Members

            /// <summary>
            /// Gets a value that indicates whether the asynchronous operation has completed.
            /// </summary>
            /// <returns>
            /// true if the operation is complete; otherwise, false.
            /// </returns>
            /// <filterpriority>2</filterpriority>
            public bool IsCompleted { get; private set; }

            /// <summary>
            /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is used to wait for an asynchronous operation to complete.
            /// </summary>
            /// <returns>
            /// A <see cref="T:System.Threading.WaitHandle"/> that is used to wait for an asynchronous operation to complete.
            /// </returns>
            /// <filterpriority>2</filterpriority>
            public WaitHandle AsyncWaitHandle { get; private set; }

            /// <summary>
            /// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
            /// </summary>
            /// <returns>
            /// A user-defined object that qualifies or contains information about an asynchronous operation.
            /// </returns>
            /// <filterpriority>2</filterpriority>
            public object AsyncState
            {
                get { return _asyncState; }
                set
                {
                    _asyncState = value;
                    CompletedSynchronously = false;
                    IsCompleted = true;
                }
            }

            /// <summary>
            /// Gets a value that indicates whether the asynchronous operation completed synchronously.
            /// </summary>
            /// <returns>
            /// true if the asynchronous operation completed synchronously; otherwise, false.
            /// </returns>
            /// <filterpriority>2</filterpriority>
            public bool CompletedSynchronously { get; private set; }

            #endregion
        }

        #endregion

        public IEnumerable<SynoItem> Browse(string itemID)
        {
            var client = new WebClient();
            if (_tokenCookie == null) throw new InvalidOperationException("Token not available : Please login to the server before issuing queries.");
            client.Headers.Set("Cookie", _tokenCookie);

            // if it's audioroot : create a well-formed uri for audioroot.
            Uri uri = new UriBuilder
                      {
                          Host = _host,
                          Port = _port,
                          Path = @"audio/webUI/audio_browse.cgi",
                          Query = string.Format("action=browse&target={0}&server=musiclib_root",itemID)                       
                      }.Uri;

            // if it is a synchronous operation : download the string right away.
            var s = client.DownloadString(uri);

            // if it's musiclib_root : the returned list is of type Album. 
            long itemCount;
            IEnumerable<SynoItem> items;

            parseSynologyItems(s, out items,out itemCount);
            return items;
            
            //"action=browse&target=musiclib_root&server=musiclib_root"
        }

        private void parseSynologyItems<T>(string jsonContent, out IEnumerable<T> items, out long itemCount)
        {
            itemCount = 0L;
            items = null;
            // if the items are Albums : 
            if (typeof(T).Equals(typeof(SynoItem)))
            {
                IEnumerable<SynoItem> albums;                
                parseSynologyAlbums(jsonContent, out albums, out itemCount);
                items = (IEnumerable<T>) albums; 
            }
        }



       


        /// <summary>
        /// Tries to request the URL: http://hamilcar.serveftp.com:5000/webman/login.cgi
        /// </summary>
        /// <param name="login">The login.</param>
        /// <param name="password">The password.</param>
        /// <returns>
        /// True if the request was successful; false otherwise.
        /// </returns>
        public string GetToken(string login, string password)
        {
            HttpWebResponse response = null;
            try
            {
                //Create request to URL.
                var request = (HttpWebRequest)WebRequest.Create(_url+"/webman/login.cgi");

                //Set request headers.
                request.Accept = "application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*";
                request.Referer = _url+"/webman/index.cgi";
                request.Headers.Set(HttpRequestHeader.AcceptLanguage, "en-US");
                request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2; OfficeLiveConnector.1.5; OfficeLivePatch.1.3)";
                request.ContentType = "application/x-www-form-urlencoded";
                request.Headers.Set(HttpRequestHeader.AcceptEncoding, "gzip, deflate");
                request.Headers.Set(HttpRequestHeader.Pragma, "no-cache");

                //Set request method
                request.Method = "POST";

                //Set request body.
                string postString = @"username="+login+"&passwd="+password;
                byte[] postBytes = System.Text.Encoding.UTF8.GetBytes(postString);
                request.ContentLength = postBytes.Length;
                Stream stream = request.GetRequestStream();
                stream.Write(postBytes, 0, postBytes.Length);
                stream.Close();

                //Get response to request.
                response = (HttpWebResponse)request.GetResponse();
                var cookies = response.Headers["Set-Cookie"];
                response.Close();
                return cookies;
            }
            catch (WebException e)
            {
                //ProtocolError indicates a valid HTTP response, but with a non-200 status code (e.g. 304 Not Modified, 404 Not Found)
                if (e.Status == WebExceptionStatus.ProtocolError) response = (HttpWebResponse)e.Response;
                else return string.Empty;
            }
            catch (Exception)
            {
                if (response != null) response.Close();
                return string.Empty;
            }

            return string.Empty;
        }

        /// <summary>
        /// Tries to request the URL
        /// </summary>
        /// <param name="response">After the function has finished, will possibly contain the response to the request.</param>
        /// <param name="cookies">The cookies.</param>
        /// <param name="searchQuery">The search query.</param>
        /// <returns>
        /// True if the request was successful; false otherwise.
        /// </returns>
        public IEnumerable<SynoTrack> RequestHamilcarServeftpCom5000BrowseCgi(string searchQuery)
        {

            //WebClient wc = new WebClient();

            //wc.Headers.Set(HttpRequestHeader.Accept, "*/*");
            //wc.Headers.Set(HttpRequestHeader.AcceptLanguage, "en-US");
            //wc.Headers.Set(HttpRequestHeader.Referer, _url + "/audio/");
            //wc.Headers.Add("x-requested-with", "XMLHttpRequest");
            //wc.Headers.Set(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded; charset=UTF-8");
            //wc.Headers.Set(HttpRequestHeader.AcceptEncoding, "gzip, deflate");
            //wc.Headers.Set(HttpRequestHeader.UserAgent, "Mydra Center");
            //wc.Headers.Set(HttpRequestHeader.Pragma, "no-cache");
            //wc.Headers.Set(HttpRequestHeader.Cookie, _tokenCookie);
            //wc.OpenReadAsync(new Uri(_url+"/audio/webUI/audio_browse.cgi"));

           

            HttpWebResponse response = null;

            IEnumerable<SynoTrack> items;

            //Create request to URL.
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_url+"/audio/webUI/audio_browse.cgi");

            //Set request headers.
            request.Accept = "*/*";
            request.Headers.Set(HttpRequestHeader.AcceptLanguage, "en-US");
            request.Referer = _url+"/audio/";
            request.Headers.Add("x-requested-with", "XMLHttpRequest");
            request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
            request.Headers.Set(HttpRequestHeader.AcceptEncoding, "gzip, deflate");
            request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2; OfficeLiveConnector.1.5; OfficeLivePatch.1.3)";
            request.Headers.Set(HttpRequestHeader.Pragma, "no-cache");
            request.Headers.Set(HttpRequestHeader.Cookie, _tokenCookie);

            //Set request method
            request.Method = "POST";

            //Set request body.
            string postString = @"action=search&target=musiclib_root&server=musiclib_root&category=all&keyword=" + searchQuery + "&start=0";
            byte[] postBytes = System.Text.Encoding.UTF8.GetBytes(postString);
            request.ContentLength = postBytes.Length;
            Stream stream = request.GetRequestStream();
            stream.Write(postBytes, 0, postBytes.Length);
            stream.Close();

            //Get response to request.
            response = (HttpWebResponse)request.GetResponse();

            var responseStream = response.GetResponseStream();

            // FIXME : Really looking forward C# 5.0 to have an Async ReadToEnd method.
            var reader = new StreamReader(responseStream);
            var content = reader.ReadToEnd();
            long count;
            parseSynologyFiles(content, out items, out count);

            return items;
        }


        /// <summary>
        /// Gets the resource.
        /// </summary>
        /// <param name="resourcePath">The resource path.</param>
        public HttpWebResponse GetResource(string resourcePath)
        {
            HttpWebResponse response = null;

            try
            {
                //Create request to URL.
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_url + "/audio/webUI/audio_stream.cgi/0.mp3?sid=RM274UM9PNSVM&action=streaming&songpath=" + resourcePath);

                //Set request headers.
                request.Accept = "*/*";
                request.Headers.Set(HttpRequestHeader.AcceptLanguage, "en-US");
                request.Referer = _url + "/audio/scripts/SoundManager2/soundmanager2_flash9.swf?1374";
                request.Headers.Add("x-flash-version", "10,1,102,64");
                request.Headers.Set(HttpRequestHeader.AcceptEncoding, "gzip, deflate");
                request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2; OfficeLiveConnector.1.5; OfficeLivePatch.1.3)";
                request.Headers.Set(HttpRequestHeader.Cookie, _tokenCookie);

                //Get response to request.
                response = (HttpWebResponse)request.GetResponse();

                
                
                return response;
            }
            catch (WebException e)
            {
                //ProtocolError indicates a valid HTTP response, but with a non-200 status code (e.g. 304 Not Modified, 404 Not Found)
                if (e.Status == WebExceptionStatus.ProtocolError) response = (HttpWebResponse)e.Response;
                return response;
            }
            catch (Exception)
            {
                if (response != null) response.Close();
                return response;
            }
        }
    }
}